import asyncio
import datetime
import time
from typing import Dict, Optional, Union
from fastapi import (
    BackgroundTasks,
    Cookie,
    FastAPI,
    File,
    Form,
    HTTPException,
    Header,
    Path,
    Query,
    Body,
    Response,
    Request,
    UploadFile,
    status,
)
from fastapi.responses import JSONResponse
from pydantic import BaseModel, Field
from fastapi.staticfiles import StaticFiles
from fastapi.openapi.docs import get_swagger_ui_html, get_redoc_html
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm


app = FastAPI(docs_url=None, redoc_url=None)
app.mount("/static", StaticFiles(directory="static"), name="static")


@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
    start_time = time.time()
    # 在请求request中设置身份信息，便于根据请求身份做不同处理 服务器本机登录 设为管理员
    if request.headers["host"] in ["127.0.0.1:8000", "localhost:8000"]:
        request.state.user = "admin"
    else:
        request.state.user = "user"

    response = await call_next(request)
    # 响应头添加自定义头 请求花费的时间
    process_time = time.time() - start_time
    response.headers["X-Process-Time"] = str(process_time)
    return response


async def send_email(email: str, msg: str):
    await asyncio.sleep(1)
    print(datetime.datetime.now())
    print(f"send email to {email}, {msg=}")


@app.get("/")
def task(bgt: BackgroundTasks):
    bgt.add_task(send_email, "1@q.cn", "hello")
    return {"now": datetime.datetime.now()}


@app.get(
    "/docs",
    include_in_schema=False,
)
async def custom_swagger_ui_html():
    return get_swagger_ui_html(
        openapi_url=app.openapi_url,
        title=app.title + " - My API",
        oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url,
        swagger_js_url="/static/swagger/swagger-ui-bundle.js",
        swagger_css_url="/static/swagger/swagger-ui.css",
        swagger_favicon_url="/static/favicon.ico",
    )


@app.get("/redoc", include_in_schema=False)
async def redoc_html():
    return get_redoc_html(
        openapi_url=app.openapi_url,
        title=app.title + " - My ReDoc",
        redoc_js_url="/static/redoc/redoc.standalone.js",
        redoc_favicon_url="/static/favicon.ico",
    )


class UserModel(BaseModel):
    username: str = Field(..., examples=["john_doe"])
    age: int = Field(..., gt=0, lt=150, examples=[30], alias="user_age")
    password: Optional[str] = Field(None, examples=["123456"], min_length=4, max_length=20)
    email: Optional[str] = Field(None, examples=["1@q.cn"], pattern=r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$")


@app.post(
    "/users",
    summary="创建用户",
    response_model=UserModel,
    response_model_exclude={"password"},
    response_model_exclude_unset=True,
    status_code=status.HTTP_201_CREATED,
    responses={
        status.HTTP_400_BAD_REQUEST: {"model": ErrorMessage},
        status.HTTP_401_UNAUTHORIZED: {"description": "认证失败"},
    },
)
async def create_user(user: UserModel):
    return user.model_dump(exclude={"password"}, by_alias=True, exclude_unset=True)


@app.post("/form", response_model=UserModel, summary="表单 demo")
def form_demo(
    name: str = Form(..., description="姓名"),
    age: int = Form(..., gt=0, lt=150, examples=[30], description="年龄"),
    email: str = Form(None, description="邮箱"),
):

    return {"name": name, "age": age, "email": email}


# 自定义异常
class UserNotFoundException(Exception):
    def __init__(self, username: str):
        self.username = username


@app.exception_handler(UserNotFoundException)
async def user_not_found_exception_handler(request: Request, exc: UserNotFoundException):
    return JSONResponse(
        status_code=status.HTTP_404_NOT_FOUND,
        content={"message": f"User {exc.username} not found"},
    )


@app.get("/echo", summary="request 对象")
async def get_request(req: Request):
    # print(req) # for debug
    # req.auth / req.user # 认证信息 需要 AuthenticationMiddleware 开启, 否则为 卡死状态
    # print(req.auth) # 报错 AssertionError: AuthenticationMiddleware must be installed to access request.auth
    # req.session 需要开启 SessionMiddleware ,
    # req.state 自定义参数存放 请求状态

    return {
        "base_url": req.base_url,
        "client": req.client,
        "cookies": req.cookies,
        "body": await req.body(),
        "headers": req.headers,
        "method": req.method,
        "path_params": req.path_params,
        "query_params": req.query_params,
        "scope": {k: str(v) for k, v in req.scope.items()},
        "url": req.url,
    }


if __name__ == "__main__":
    import uvicorn

    uvicorn.run(app="base_demo:app", host="127.0.0.1", port=8000, reload=True)
